package Translate;

import Symbol.Symbol;
import Temp.Temp;
import Temp.Label;
import Temp.TempList;
import Tree.LABEL;

public class Translate {
	public Frame.Frame frame;

	public Translate(Frame.Frame f) {
		frame = f;
	}

	private Frag frags;

	public void procEntryExit(Level level, Exp body) {
		Frame.Frame myframe = level.frame;
		Tree.Exp bodyExp = body.unEx();
		Tree.Stm bodyStm;
		if (bodyExp != null)
			bodyStm = MOVE(TEMP(myframe.RV()), bodyExp);
		else
			bodyStm = body.unNx();
		ProcFrag frag = new ProcFrag(myframe.procEntryExit1(bodyStm), myframe);
		frag.next = frags;
		frags = frag;
	}

	public Frag getResult() {
		return frags;
	}

	private static Tree.Exp CONST(int value) {
		return new Tree.CONST(value);
	}

	private static Tree.Exp NAME(Label label) {
		return new Tree.NAME(label);
	}

	private static Tree.Exp TEMP(Temp temp) {
		return new Tree.TEMP(temp);
	}

	private static Tree.Exp BINOP(int binop, Tree.Exp left, Tree.Exp right) {
		return new Tree.BINOP(binop, left, right);
	}

	private static Tree.Exp MEM(Tree.Exp exp) {
		return new Tree.MEM(exp);
	}

	private static Tree.Exp CALL(Tree.Exp func, Tree.ExpList args) {
		return new Tree.CALL(func, args);
	}

	private static Tree.Exp ESEQ(Tree.Stm stm, Tree.Exp exp) {
		if (stm == null)
			return exp;
		return new Tree.ESEQ(stm, exp);
	}

	private static Tree.Stm MOVE(Tree.Exp dst, Tree.Exp src) {
		return new Tree.MOVE(dst, src);
	}

	private static Tree.Stm EXPR(Tree.Exp exp) {
		return new Tree.EXPR(exp);
	}

	private static Tree.Stm JUMP(Label target) {
		return new Tree.JUMP(target);
	}

	private static Tree.Stm CJUMP(int relop, Tree.Exp l, Tree.Exp r, Label t,
			Label f) {
		return new Tree.CJUMP(relop, l, r, t, f);
	}

	private static Tree.Stm SEQ(Tree.Stm left, Tree.Stm right) {
		if (left == null)
			return right;
		if (right == null)
			return left;
		return new Tree.SEQ(left, right);
	}

	private static Tree.Stm LABEL(Label label) {
		return new Tree.LABEL(label);
	}

	private static Tree.ExpList ExpList(Tree.Exp head, Tree.ExpList tail) {
		return new Tree.ExpList(head, tail);
	}

	private static Tree.ExpList ExpList(Tree.Exp head) {
		return ExpList(head, null);
	}

	private static Tree.ExpList ExpList(ExpList exp) {
		if (exp == null)
			return null;
		return ExpList(exp.head.unEx(), ExpList(exp.tail));
	}

	public Exp Error() {
		return new Ex(CONST(0));
	}

	public Exp SimpleVar(Access access, Level level) {
		Level d = access.home;
		Tree.Exp base = TEMP(level.frame.FP());
		for (; level != d; level = level.parent) {
			base = level.frame.formals.head.exp(base);
		}
		return new Ex(access.acc.exp(base));
	}

	public Exp FieldVar(Exp record, int index) {
		return new Ex(BINOP(0, MEM(record.unEx()), CONST(index)));
	}

	public Exp SubscriptVar(Exp array, Exp index) {
		return new Ex(BINOP(0, MEM(array.unEx()), index.unEx()));
	}

	public Exp NilExp() {
		return new Nx(null);
	}

	public Exp IntExp(int value) {
		return new Ex(CONST(value));
	}

	private java.util.Hashtable strings = new java.util.Hashtable();

	public Exp StringExp(String lit) {
		String u = lit.intern();
		Label lab = (Label) strings.get(u);
		if (lab == null) {
			lab = new Label();
			strings.put(u, lab);
			DataFrag frag = new DataFrag(frame.string(lab, u));
			frag.next = frags;
			frags = frag;
		}
		return new Ex(NAME(lab));
	}

	private Tree.Exp CallExp(Symbol f, ExpList args, Level from) {
		return frame.externalCall(f.toString(), ExpList(args));
	}

	private Tree.Exp CallExp(Level f, ExpList args, Level from) {
		return frame.externalCall(f.name().toString(), ExpList(args));
	}

	public Exp FunExp(Symbol f, ExpList args, Level from) {
		return new Ex(CallExp(f, args, from));
	}

	public Exp FunExp(Level f, ExpList args, Level from) {
		return new Ex(CallExp(f, args, from));
	}

	public Exp ProcExp(Symbol f, ExpList args, Level from) {
		return new Nx(EXPR(CallExp(f, args, from)));
	}

	public Exp ProcExp(Level f, ExpList args, Level from) {
		return new Nx(EXPR(CallExp(f, args, from)));
	}

	public Exp OpExp(int op, Exp left, Exp right) {
		switch (op) {
		case 0:
		case 1:
		case 2:
		case 3:
			return new Ex(BINOP(op, left.unEx(), right.unEx()));
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
			return new Ex(BINOP(op, left.unEx(), right.unEx()));
			// return new RelCx(op - 4, left.unEx(), right.unEx());
		}
		return Error();
	}

	public Exp StrOpExp(int op, Exp left, Exp right) {
		switch (op) {
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
			return new Ex(BINOP(op, left.unEx(), right.unEx()));
		}
		return Error();
	}

	public Exp RecordExp(ExpList init) {
		return new Nx(null);
	}

	public Exp SeqExp(ExpList e) {
		Nx exp = null;
		if (e != null)
			exp = new Nx(null);
		while (e != null) {
			if (e.head != null)
				exp = new Nx(SEQ(e.head.unNx(), exp.unNx()));
			e = e.tail;
		}
		return exp;
	}

	public Exp AssignExp(Exp lhs, Exp rhs) {
		return new Nx(MOVE(lhs.unEx(), rhs.unEx()));
	}

	public Exp IfExp(Exp cc, Exp aa, Exp bb) {
		return new IfThenElseExp(cc, aa, bb);
	}

	public Exp WhileExp(Exp test, Exp body, Label done) {
		Label start = new Label();
		Label loopBody = new Label();
		return new Nx(SEQ(SEQ(SEQ(SEQ(new Tree.LABEL(start), test.unCx(
				loopBody, done)), new Tree.LABEL(loopBody)), body.unNx()),
				JUMP(start)));
	}

	public Exp ForExp(Access i, Exp lo, Exp hi, Exp body, Label done) {
		Access limit = i;
		Level d = i.home;
		return (new Nx(SEQ(AssignExp(SimpleVar(i, d), lo).unNx(), AssignExp(
				SimpleVar(limit, d), hi).unNx())));
	}

	public Exp BreakExp(Label done) {
		return new Nx(JUMP(done));
	}

	public Exp LetExp(ExpList lets, Exp body) {
		ExpList tmp = null;
		ExpList iter = lets;
		Exp exp = body;
		while (iter != null) {
			tmp = new ExpList(iter.head, tmp);
			iter = iter.tail;
		}
		if (tmp != null) {
			exp = new Nx(SEQ(exp.unNx(), tmp.head.unNx()));
			tmp = tmp.tail;
		}
		while (tmp != null) {
			exp = new Nx(SEQ(tmp.head.unNx(), exp.unNx()));
			tmp = tmp.tail;
		}
		return (exp);
	}

	public Exp ArrayExp(Exp size, Exp init) {
		Label initArray = new Label("initArray");
		return new Ex(new Tree.CALL(NAME(initArray), new Tree.ExpList(size
				.unEx(), new Tree.ExpList(init.unEx(), null))));
	}

	public Exp VarDec(Access a, Exp init) {
		return new Nx(MOVE(a.acc.exp(TEMP(a.home.frame.FP())), init.unEx()));
	}

	public Exp TypeDec() {
		return new Nx(null);
	}

	public Exp FunctionDec() {
		return new Nx(null);
	}
}
